﻿//-----------------------------------------------------------------------
// <copyright file="DragDockPanel.cs" company="Microsoft Corporation copyright 2008.">
// (c) 2008 Microsoft Corporation. All rights reserved.
// This source is subject to the Microsoft Public License.
// See http://www.microsoft.com/resources/sharedsource/licensingbasics/sharedsourcelicenses.mspx.
// </copyright>
// <date>15-Sep-2008</date>
// <author>Martin Grayson</author>
// <summary>A draggable, dockable, expandable panel class.</summary>
//-----------------------------------------------------------------------
namespace Blacklight.Silverlight.Controls
{
    using System;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Ink;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;
    using System.Windows.Controls.Primitives;

    /// <summary>
    /// A draggable, dockable, expandable panel class.
    /// </summary>
    public class DragDockPanel : AnimatedContentControl
    {
        #region Private members

        private ToggleButton _maximizeToggle;
        private ScaleTransform _scaleTransform;
        private ContentPresenter _contentPresenter;

        /// <summary>
        /// Stores the current z-index
        /// </summary>
        private static int currentZIndex = 1; 

        /// <summary>
        /// Is dragging flag.
        /// </summary>
        private bool dragging = false;

        /// <summary>
        /// Stores the last drag position.
        /// </summary>
        private Point lastDragPosition;

        /// <summary>
        /// Ignore the last uncheck event flag.
        /// </summary>
        private bool ignoreUnCheckedEvent = false;

        /// <summary>
        /// Dragging enabled flag.
        /// </summary>
        private bool draggingEnabled = false;

        /// <summary>
        /// Panel maximised flag.
        /// </summary>
        private bool maximized = false;
        #endregion
        
        /// <summary>
        /// Drag dock panel constructor.
        /// </summary>
        public DragDockPanel()
        {
            this.DefaultStyleKey = typeof(DragDockPanel);
        }

        #region DPs

        public static readonly DependencyProperty HeaderProperty =
            DependencyProperty.Register("Header", typeof(String), typeof(DragDockPanel), new PropertyMetadata("Header"));

        public String Header
        {
            get { return (String)GetValue(HeaderProperty); }
            set { SetValue(HeaderProperty, value); }
        }

        #endregion

        #region Public events
        /// <summary>
        /// The drag started event.
        /// </summary>
        public event DragEventHander DragStarted;

        /// <summary>
        /// The drag moved event.
        /// </summary>
        public event DragEventHander DragMoved;

        /// <summary>
        /// The drag finished event.
        /// </summary>
        public event DragEventHander DragFinished;

        /// <summary>
        /// The maxmised event.
        /// </summary>
        public event EventHandler Maximized;

        /// <summary>
        /// The minimised event.
        /// </summary>
        public event EventHandler Minimized;
        #endregion

        #region Public members
        /// <summary>
        /// Gets or sets a value indicating whether the dragging is enabled.
        /// </summary>
        public bool DraggingEnabled
        {
            get { return this.draggingEnabled; }
            set { this.draggingEnabled = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether or not the panel is maximised (and updates the toggle button UI)
        /// </summary>
        public bool PanelMaximized
        {
            get 
            { 
                return this.maximized; 
            }

            set
            {
                this.maximized = value;

                if (_maximizeToggle != null)
                {
                    this.ignoreUnCheckedEvent = true;
                    _maximizeToggle.IsChecked = this.maximized;

                    _maximizeToggle.Visibility = value ? Visibility.Collapsed : Visibility.Visible;
                }

                if (_contentPresenter != null)
                {
                    _contentPresenter.Visibility = value ? Visibility.Visible : Visibility.Collapsed;
                }

                //if (_scaleTransform != null)
                //{
                //    Double scale = 1;
                //    if (!this.maximized)
                //    {
                //        var content = this.GetTemplateChild("PART_Content") as ContentPresenter;
                //        if (content.Content is FrameworkElement)
                //        {
                //            var element = (FrameworkElement)content.Content;
                //            Double scaleX = element.MinWidth / Width;
                //            Double scaleY = element.MinHeight / Height;

                //            scale = Math.Min(scaleX, scaleY);
                //            content.MinWidth = element.MinWidth;
                //            content.MinHeight = element.MinHeight;
                //            content.UpdateLayout();
                //        }
                //    }

                //    _scaleTransform.ScaleX = scale;
                //    _scaleTransform.ScaleY = scale;
                //}
            }
        }
        #endregion

        /// <summary>
        /// Gets called once the template is applied so we can fish out the bits
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            _scaleTransform = this.GetTemplateChild("PART_Scale") as ScaleTransform;
            _contentPresenter = this.GetTemplateChild("PART_Content") as ContentPresenter;

            FrameworkElement gripBar =
                this.GetTemplateChild("PART_GripBar") as FrameworkElement;

            if (gripBar != null)
            {
                gripBar.MouseLeftButtonDown +=
                    new MouseButtonEventHandler(this.GripBar_MouseLeftButtonDown);

                gripBar.MouseMove +=
                   new MouseEventHandler(this.GripBar_MouseMove);

                gripBar.MouseLeftButtonUp +=
                    new MouseButtonEventHandler(this.GripBar_MouseLeftButtonUp);
            }

            _maximizeToggle =
                this.GetTemplateChild("PART_MaximizeToggle") as ToggleButton;

            if (_maximizeToggle != null)
            {
                _maximizeToggle.Checked +=
                    new RoutedEventHandler(this.MaximizeToggle_Checked);
                _maximizeToggle.Unchecked +=
                    new RoutedEventHandler(this.MaximizeToggle_Unchecked);
            }

            if (PanelMaximized)
            {
                _maximizeToggle.Visibility = Visibility.Collapsed;
                _contentPresenter.Visibility = Visibility.Visible;
            }
            else
            {
                _maximizeToggle.Visibility = Visibility.Visible;
                _contentPresenter.Visibility = Visibility.Collapsed;
            }
        }

        #region Maximize events
        /// <summary>
        /// Fires the minimised event.
        /// </summary>
        /// <param name="sender">The maximised toggle.</param>
        /// <param name="e">Routed event args.</param>
        private void MaximizeToggle_Unchecked(object sender, RoutedEventArgs e)
        {
            if (!this.ignoreUnCheckedEvent)
            {
                this.PanelMaximized = false;

                // Fire the panel minimized event
                if (this.Minimized != null)
                {
                    this.Minimized(this, e);
                }
            }
            else
            {
                this.ignoreUnCheckedEvent = false;
            }

            _maximizeToggle.Visibility = Visibility.Visible;
        }

        /// <summary>
        /// Fires the maximised event.
        /// </summary>
        /// <param name="sender">The maximised toggle.</param>
        /// <param name="e">Routed event args.</param>
        private void MaximizeToggle_Checked(object sender, RoutedEventArgs e)
        {
            // Bring the panel to the front
            Canvas.SetZIndex(this, currentZIndex++);

            this.ignoreUnCheckedEvent = false;
            PanelMaximized = true;

            // Fire the panel maximized event
            if (this.Maximized != null)
            {
                this.Maximized(this, e);                
            }

            if (_maximizeToggle != null)
            {
                _maximizeToggle.Visibility = Visibility.Collapsed;
            }
        }

        public void Maximize()
        {
            MaximizeToggle_Checked(this, null);   
        }

        #endregion

        public override void SizeAnimationCompleted()
        {
            base.SizeAnimationCompleted();

            if (_contentPresenter != null && !PanelMaximized)
            {
                _contentPresenter.Visibility = Visibility.Collapsed;
            }
        }

        #region Dragging events
        /// <summary>
        /// Starts the dragging.
        /// </summary>
        /// <param name="sender">The grip bar.</param>
        /// <param name="e">Mouse button event args.</param>
        private void GripBar_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (this.draggingEnabled)
            {
                // Bring the panel to the front
                Canvas.SetZIndex(this, currentZIndex++);

                // Capture the mouse
                ((FrameworkElement)sender).CaptureMouse();

                // Store the start position
                this.lastDragPosition = e.GetPosition(sender as UIElement);

                // Set dragging to true
                this.dragging = true;

                // Fire the drag started event
                if (this.DragStarted != null)
                {
                    this.DragStarted(this, new DragEventArgs(0, 0, e));
                }
            }
        }

        /// <summary>
        /// Ends the dragging.
        /// </summary>
        /// <param name="sender">The grip bar.</param>
        /// <param name="e">Mouse button event args.</param>
        private void GripBar_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (this.draggingEnabled)
            {
                // Capture the mouse
                ((FrameworkElement)sender).ReleaseMouseCapture();

                // Set dragging to true
                this.dragging = false;

                Point position = e.GetPosition(sender as UIElement);

                // Fire the drag finished event
                if (this.DragFinished != null)
                {
                    this.DragFinished(this, new DragEventArgs(position.X - this.lastDragPosition.X, position.Y - this.lastDragPosition.Y, e));
                }
            }
        }

        /// <summary>
        /// Drags the panel (if the panel is being dragged).
        /// </summary>
        /// <param name="sender">The grip bar.</param>
        /// <param name="e">Mouse event args.</param>
        private void GripBar_MouseMove(object sender, MouseEventArgs e)
        {
            if (this.dragging)
            {
                Point position = e.GetPosition(sender as UIElement);

                // Move the panel
                Canvas.SetLeft(
                    this,
                    Canvas.GetLeft(this) + position.X - this.lastDragPosition.X);

                Canvas.SetTop(
                    this,
                    Canvas.GetTop(this) + position.Y - this.lastDragPosition.Y);

                // Fire the drag moved event
                if (this.DragMoved != null)
                {
                    this.DragMoved(this, new DragEventArgs(position.X - this.lastDragPosition.X, position.Y - this.lastDragPosition.Y, e));
                }

                // Update the last mouse position
                this.lastDragPosition = e.GetPosition(sender as UIElement);
            }
        }
        #endregion
    }
}
